package com.baoyouqun.base.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;

@Slf4j
public class ConvertUtils {


    /**
     * 封装BeanUtils.copyProperties 属性转换
     * 包含id
     *
     * @param source 源对象
     * @param target 目标对象
     * @return obj   转换后的对象
     */
    public static <T, E> T copyBeanNotNull(E source, T target) {
        return copyBeanNotNull(source, target, true);
    }


    /**
     * 封装BeanUtils.copyProperties 数组转换
     *
     * @param source    源对象
     * @param target    目标对象
     * @param containId 是否包含id
     * @return obj   转换后的对象
     */
    public static <T, E> T copyBeanNotNull(E source, T target, boolean containId) {
        if (source == null) {
            return null;
        }
        List<Field> fields_source = getAllFields(source.getClass());
        List<Field> fields_target = getAllFields(target.getClass());
        for (Field field_target :
                fields_target) {
            boolean isStatic = Modifier.isStatic(field_target.getModifiers());
            if (isStatic || (!containId && "id".equals(field_target.getName()))) {
                continue;
            }
            for (Field field_source :
                    fields_source) {
                boolean isStatic_ = Modifier.isStatic(field_source.getModifiers());
                if (isStatic_ || (!containId && "id".equals(field_source.getName()))) {
                    continue;
                }
                if (field_target.getName().equals(field_source.getName()) &&
                        field_source.getType().equals(field_target.getType())) {
                    try {
                        field_source.setAccessible(true);
                        field_target.setAccessible(true);
                        Object value = field_source.get(source);
                        if (Objects.isNull(value)) {
                            break;
                        }
                        field_target.set(target, value);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    }
                    break;
                }
            }
        }
        return target;
    }


    public static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        return fieldList;
    }

    public static List<String> getAllFieldNames(Class<?> clazz) {
        List<String> fieldNameList = new ArrayList<>();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        for (Field field : fieldList) {
            fieldNameList.add(field.getName());
        }
        return fieldNameList;
    }

    public static <T extends Annotation> boolean getFieldAnnotation(Class<?> clazz, Class<T> annotation) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        for (Field field :
                fieldList) {
            try {
                T t = field.getAnnotation(annotation);
                if (Objects.nonNull(t)) {
                    return true;
                }
            } catch (Exception e) {
                log.error("getFieldAnnotation", e);
            }
        }
        return false;
    }


    public static Field getAllField(Class<?> clazz, String fieldName) {
        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field_temp :
                    fields) {
                if (fieldName.equals(field_temp.getName())) {
                    return field_temp;
                }
            }
            clazz = clazz.getSuperclass();
        }
        return null;
    }

    /**
     * 封装BeanUtils.copyProperties 数组转换
     *
     * @param source 源对象
     * @param target 目标对象
     * @param <T>    目标对象类型
     * @return obj   转换后的对象
     */
    public static <T> T copyBean(Object source, Class<T> target) {
        if (source == null) {
            return null;
        }
        T clazz;
        try {
            Constructor<T> c0 = target.getDeclaredConstructor();
            c0.setAccessible(true);
            clazz = c0.newInstance();
            BeanUtils.copyProperties(source, clazz);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return clazz;
    }


    /**
     * 封装BeanUtils.copyProperties 数组转换
     *
     * @param resourceList 源数组
     * @param target       目标对象
     * @param baseClass    目标父对象
     * @param <E>          目标父对象类型
     * @param <T>          目标对象类型
     * @return targetList  转换后的数组
     */
    public static <E, T extends E> List<E> copyBeanList(List<?> resourceList, Class<T> target, Class<E> baseClass) {
        List<T> temp = copyBeanList(resourceList, target);
        List<E> result = new ArrayList<>(temp);
        return result;
    }


    /**
     * 封装BeanUtils.copyProperties 数组转换
     *
     * @param resourceList 源数组
     * @param target       目标对象
     * @param <T>          目标对象类型
     * @return targetList  转换后的数组
     */
    public static <T> List<T> copyBeanList(Collection<?> resourceList, Class<T> target) {
        List<T> targetList = new LinkedList<>();
        if (null == resourceList || resourceList.isEmpty()) {
            return targetList;
        }
        resourceList.forEach(ele -> {
            T clazz = null;
            try {
                Constructor<T> c0 = target.getDeclaredConstructor();
                c0.setAccessible(true);
                clazz = c0.newInstance();
            } catch (InstantiationException | IllegalAccessException | NoSuchMethodException ex) {
                ex.printStackTrace();
            } catch (InvocationTargetException ex) {
                throw new RuntimeException(ex);
            }
            assert clazz != null;
            BeanUtils.copyProperties(ele, clazz);
            targetList.add(clazz);
        });
        return targetList;
    }

}
